package belf.migrate.engine.job.structure.helper;

import belf.migrate.api.exception.*;
import belf.migrate.api.model.QualifiersModel;
import belf.migrate.api.table.schema.*;
import belf.migrate.api.taskconf.JobContext;
import belf.migrate.api.type.DbObjectType;
import belf.migrate.api.type.LogLevelType;
import belf.migrate.api.util.*;
import belf.migrate.engine.job.JobResult;
import belf.migrate.api.job.Job;
import belf.migrate.engine.job.JobResultItem;
import belf.migrate.api.taskconf.Task;
import belf.migrate.api.util.ExceptionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.util.Date;

import static belf.migrate.api.job.JobCode.*;

@Slf4j
public class CreateTableTool extends TableTool {

    public CreateTableTool(JobContext jobContext, Job job) {
        super(jobContext, job);
    }

    public JobResult run() {
        PostLog.sendLogModel(
                job.getJobId(),
                job.getTask().getTaskType().getName(),
                "创建表开始！",
                LogLevelType.INFO.getType(),
                EXECUTING,
                jobContext.getProgress());

        JobResult jobResult = new JobResult(SUCCESS, "创建选中的表成功");
        AbstractJdbcCatalog sinkCatalog = jobContext.getSinkCatalog();
        AbstractJdbcCatalog sourceCatalog = jobContext.getSourceCatalog();
        QualifiersModel qualifiersModel = getSinkQualifiersModel();
        for (CatalogTable catalogTable : jobContext.getSourcetableMap().values()) {
            String startTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
            TablePath sourceTablePath = catalogTable.getTablePath();
            TablePath sinkTablePath = TablePath.of(
                    jobContext.getTaskConf().getSinkConf().getDatabaseName(),
                    jobContext.getTaskConf().getSinkConf().getSchemaName(),
                    sourceTablePath.getTableName(),
                    qualifiersModel);

            // 若限定了要处理的表名称
            if (job.getMissions() != null && !job.getMissions().isEmpty()) {
                if (TableNameComparator.containsTable(job.getMissions(), sourceTablePath.getTableName())) {
                    jobContext.addProgress(DbObjectType.TABLE.getTypeName(), job.getMissions().size());
                    JobResultItem jobResultItem = process(sinkCatalog, sourceTablePath, catalogTable, startTime, sourceCatalog, qualifiersModel, sinkTablePath);
                    jobResult.addItem(jobResultItem);
                    if (jobResultItem.getCode() != SUCCESS
                            && job.getTask().getTaskPolicy() == Task.TaskPolicy.ERROR_STOP) {
                        break;  //创建表遇到错误即返回
                    }
                }
            } else { // 若没有限定表，则是处理所有表
                jobContext.addProgress(DbObjectType.TABLE.getTypeName(), jobContext.getSourcetableMap().size());
                JobResultItem jobResultItem = process(sinkCatalog, sourceTablePath, catalogTable, startTime, sourceCatalog, qualifiersModel, sinkTablePath);
                jobResult.addItem(jobResultItem);
                if (jobResultItem.getCode() != SUCCESS
                        && job.getTask().getTaskPolicy() == Task.TaskPolicy.ERROR_STOP) {
                    break; //创建表遇到错误即返回
                }
            }
        }
        if (jobResult.getFailtures() > 0) {
            jobResult.setCode(CREATE_TABLE_ERROR);
            jobResult.setMessage("创建选中的表失败");
            log.error("Create Some Tables Of {} Failed.", jobContext.getValidSinkDatabase());
        } else {
            log.info("Create All Tables Of {} Successfully.", jobContext.getValidSinkDatabase());
        }
        PostLog.sendLogModel(
                job.getJobId(),
                job.getTask().getTaskType().getName(),
                "创建表结束！",
                LogLevelType.INFO.getType(),
                EXECUTING,
                jobContext.getProgress());
        return jobResult;
    }

    private JobResultItem process(
            Catalog catalog,
            TablePath sourceTablePath,
            CatalogTable catalogTable,
            String startTime,
            AbstractJdbcCatalog sourceCatalog,
            QualifiersModel qualifiersModel,
            TablePath sinkTablePath) {

        String tableName = sourceTablePath.getTableName();
        JobResultItem jobResultItem = new JobResultItem(
                tableName,
                sinkTablePath.getCaseTableName(),
                SUCCESS,
                "创建表" + sourceTablePath.getTableName() + "成功");
        jobResultItem.setType(DbObjectType.TABLE.getTypeName());
        jobResultItem.setStartTime(startTime);
        String sinkDdl = null;
        try {
            sinkDdl = catalog.getCreateTableSql(sinkTablePath, catalogTable, jobContext.getTaskConf());
            catalog.createTable(sinkTablePath, catalogTable, false, qualifiersModel, jobContext.getTaskConf());
//            catalog.executeDdl(sinkTablePath, sinkDdl);
            jobResultItem.setSinkDdl(sinkDdl);
            log.info("Table {} created!", catalogTable.getTablePath());
        } catch (TableAlreadyExistException e) {
            log.error("Table Already Exist: {}", sinkTablePath.getTableName());
            String endTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
            String sourceDdl = null;
            try {
                sourceDdl = sourceCatalog.showCreateTable(sourceTablePath);
            } catch (TableNotExistException ex) {
                log.error("创建表时，获取源表ddl失败");
            }
            PostLog.sendLogModel(
                    job.getJobId(),
                    job.getTask().getTaskType().getName(),
                    "表" + sourceTablePath.getTableName() + "已经存在",
                    LogLevelType.ERROR.getType(),
                    EXECUTING,
                    jobContext.getProgress());

            return new JobResultItem(sourceTablePath.getTableName(),
                    sinkTablePath.getCaseTableName(),
                    CREATE_TABLE_ERROR,
                    "表" + sourceTablePath.getTableName() + "已经存在",
                    ExceptionUtils.toString(e),
                    DbObjectType.TABLE.getTypeName(),
                    startTime,
                    endTime,
                    sourceDdl,
                    sinkDdl);
        } catch (CatalogException e) {
            log.error("Create Table {} Error:\n{}", sinkTablePath.getTableName(), sinkDdl);
            String endTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
            String sourceDdl = null;
            try {
                sourceDdl = sourceCatalog.showCreateTable(sourceTablePath);
            } catch (TableNotExistException ex) {
                log.error("创建表时，获取源表ddl失败");
            }

            PostLog.sendLogModel(
                    job.getJobId(),
                    job.getTask().getTaskType().getName(),
                    String.format("创建表%s, 发现目标端%s未适配相应功能", tableName, catalogTable.getCatalogName()),
                    LogLevelType.ERROR.getType(),
                    EXECUTING,
                    jobContext.getProgress());
            return new JobResultItem(sourceTablePath.getTableName(),
                    sinkTablePath.getCaseTableName(),
                    CREATE_TABLE_ERROR,
                    String.format("创建表%s, 发现目标端%s未适配相应功能", tableName, catalogTable.getCatalogName()),
                    ExceptionUtils.toString(e),
                    DbObjectType.TABLE.getTypeName(),
                    startTime,
                    endTime,
                    sourceDdl,
                    sinkDdl);
        } catch (DatabaseNotExistException e) {
            String endTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
            String sourceDdl = null;
            try {
                sourceDdl = sourceCatalog.showCreateTable(sourceTablePath);
            } catch (TableNotExistException ex) {
                log.error("创建表时，获取源表ddl失败");
            }

            PostLog.sendLogModel(
                    job.getJobId(),
                    job.getTask().getTaskType().getName(),
                    "目标端数据库不存在",
                    LogLevelType.ERROR.getType(),
                    EXECUTING,
                    jobContext.getProgress());
            return new JobResultItem(sourceTablePath.getTableName(),
                    sinkTablePath.getCaseTableName(),
                    CREATE_TABLE_ERROR,
                    "目标端数据库不存在",
                    ExceptionUtils.toString(e),
                    DbObjectType.TABLE.getTypeName(),
                    startTime,
                    endTime,
                    sourceDdl,
                    sinkDdl);
        } catch (CreateTableException | DataTypeMappingNotExistException e) {
            String endTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
            String sourceDdl = null;
            try {
                sourceDdl = sourceCatalog.showCreateTable(sourceTablePath);
            } catch (TableNotExistException ex) {
                log.error("创建表时，获取源表ddl失败");
            }
//            e.printStackTrace();
            PostLog.sendLogModel(
                    job.getJobId(),
                    job.getTask().getTaskType().getName(),
                    e.getMessage(),
                    LogLevelType.ERROR.getType(),
                    EXECUTING,
                    jobContext.getProgress());
            return new JobResultItem(sourceTablePath.getTableName(),
                    sinkTablePath.getCaseTableName(),
                    CREATE_TABLE_ERROR,
                    e.getMessage(),
                    ExceptionUtils.toString(e),
                    DbObjectType.TABLE.getTypeName(),
                    startTime,
                    endTime,
                    sourceDdl,
                    sinkDdl);
        }

        try {
            catalog.createIndex(sinkTablePath, catalogTable, false, jobContext.getTaskConf());
            log.info("Table {} index created!", catalogTable.getTablePath());
        } catch (IndexAlreadyExistException e) {
            String endTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
            String sourceDdl = null;
            try {
                sourceDdl = sourceCatalog.showCreateTable(sourceTablePath);
            } catch (TableNotExistException ex) {
                log.error("创建表时，获取源表ddl失败");
            }
            e.printStackTrace();
            PostLog.sendLogModel(
                    job.getJobId(),
                    job.getTask().getTaskType().getName(),
                    "目标端同名的索引已经存在",
                    LogLevelType.ERROR.getType(),
                    EXECUTING,
                    jobContext.getProgress());
            return new JobResultItem(sourceTablePath.getTableName(),
                    sinkTablePath.getCaseTableName(),
                    CREATE_INDEX_ERROR,
                    "目标端同名的索引已经存在",
                    ExceptionUtils.toString(e),
                    DbObjectType.TABLE.getTypeName(),
                    startTime,
                    endTime,
                    sourceDdl,
                    sinkDdl);
        } catch (CreateIndexException e) {
            String endTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
            String sourceDdl = null;
            try {
                sourceDdl = sourceCatalog.showCreateTable(sourceTablePath);
            } catch (TableNotExistException ex) {
                log.error("创建表时，获取源表ddl失败");
            }
            e.printStackTrace();
            PostLog.sendLogModel(
                    job.getJobId(),
                    job.getTask().getTaskType().getName(),
                    "创建索引失败",
                    LogLevelType.ERROR.getType(),
                    EXECUTING,
                    jobContext.getProgress());
            return new JobResultItem(sourceTablePath.getTableName(),
                    sinkTablePath.getCaseTableName(),
                    CREATE_INDEX_ERROR,
                    "创建索引失败",
                    ExceptionUtils.toString(e),
                    DbObjectType.TABLE.getTypeName(),
                    startTime,
                    endTime,
                    sourceDdl,
                    sinkDdl);
        }

        PostLog.sendLogModel(
                job.getJobId(),
                job.getTask().getTaskType().getName(),
                String.format("创建表 %s 成功", sourceTablePath.getTableName()),
                LogLevelType.INFO.getType(),
                EXECUTING,
                jobContext.getProgress());
        jobResultItem.setEndTime(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS"));
        return jobResultItem;
    }
}
